package com.fjwt.gz.zhyx;


import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;

import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;

/**
 * AES加密
 * @author wangdongyang
 * @date 2020/9/4
 */
@Slf4j
public class AESUtils {

    private static final String IV = "abcdefghijklmnop";
    private static final int DEFAULT_KEY_SIZE = 128;
    private static final int DEFAULT_ITERATION_COUNT = 10000;
    private final Charset defaultCharset;
    private final int keySize;
    private final int iterationCount;
    private Cipher cipher;

    static {
        StringBuffer sb7 = new StringBuffer(32);// = hnyzjraeskeyW87d
        sb7
                .append('h').append('n').append('y').append('z')
                .append('j').append('r').append('a').append('e')
                .append('s').append('k').append('e').append('y');
        AES_ENCRYPT_KEY = sb7.toString();
    }
    public static final String AES_ENCRYPT_KEY ;

    private AESUtils(int keySize, int iterationCount) {
        this.defaultCharset = Charset.forName("UTF-8");
        this.keySize = keySize;
        this.iterationCount = iterationCount;

        try {
            this.cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        } catch (NoSuchAlgorithmException e) {
//            e.printStackTrace();
            log.error("produce a exception " + e.getMessage());
        } catch (NoSuchPaddingException e) {
//            e.printStackTrace();
            log.error("produce a exception " + e.getMessage());
        }
    }

    /**
     * 解密,对文件进行解密
     *
     * keyFilePath 密钥存放路径
     * encodeFile 要解密的文件
     * decodeFile 解密后的文件
     */
    public static void getAESDecode(String keyFilePath,String encodeFile, String decodeFile) {
        Cipher cipher = getCipher(keyFilePath, Cipher.DECRYPT_MODE);
        if (cipher == null){
            return;
        }
        readAndWriteJm(encodeFile, decodeFile, cipher);
    }

    private static Cipher getCipher(String keyFilePath, int type){
        Cipher cipher = null;
        try {
            //读取保存密钥的文件
            File file = new File(keyFilePath);
            byte[] key = new byte[(int) file.length()];
            FileInputStream fis = new FileInputStream(file);
            fis.read(key);
            //根据密钥文件构建一个AES密钥
            SecretKeySpec sKeySpec = new SecretKeySpec(key, "AES");
            //实例化一个密码器CBC模式的
            cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            //初始化密码器
            cipher.init(type, sKeySpec, new IvParameterSpec(IV.getBytes()));
        } catch (Exception e) {
            log.error("文件解密异常:",e);
            return null;
        }
        return cipher;
    }

    private static void readAndWriteJm(String inputFile, String outputFile, Cipher cipher){
        //加密文件流
        FileInputStream inputStream = null;
        //解密文件流
        FileOutputStream outputStream = null;
        //以解密流写出文件
        CipherOutputStream cipherOutputStream = null;
        try {
            inputStream = new FileInputStream(inputFile);
            outputStream = new FileOutputStream(outputFile);
            cipherOutputStream = new CipherOutputStream(outputStream, cipher);
            byte[] buffer = new byte[1024];
            int r;
            while ((r = inputStream.read(buffer)) >= 0) {
                cipherOutputStream.write(buffer, 0, r);
            }
        }catch (Exception e){
            log.error("文件写入异常：",e);
        }finally {
            if (cipherOutputStream != null) {
                try {
                    cipherOutputStream.close();
                } catch (IOException e) {
                    log.error("关闭流异常1：",e);
                }
            }
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    log.error("关闭流异常2：",e);
                }
            }
            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    log.error("关闭流异常3：",e);
                }
            }

        }
    }


    public static AESUtils getInstance() {
        return getInstance(DEFAULT_KEY_SIZE, DEFAULT_ITERATION_COUNT);
    }

    public static AESUtils getInstance(int keySize, int iterationCount) {
        return new AESUtils(keySize, iterationCount);
    }


    public String decrypt( String cipherText) {
        SecretKey key = this.generateKey("3FF2EC019C627B945225DEBAD71A01B6985FE84C95A70EB132882F88C0A59A55", AES_ENCRYPT_KEY);
        byte[] decrypted = this.doFinal(2, key, "F27D5C9927726BCEFE7510B1BDD3D137", Base64.decodeBase64(cipherText));
        return new String(decrypted, this.defaultCharset);
    }

    public String encrypt( String plaintext) {
        SecretKey key = this.generateKey("3FF2EC019C627B945225DEBAD71A01B6985FE84C95A70EB132882F88C0A59A55", AES_ENCRYPT_KEY);
        byte[] encrypted = this.doFinal(1, key, "F27D5C9927726BCEFE7510B1BDD3D137", plaintext.getBytes(this.defaultCharset));
        return Base64.encodeBase64URLSafeString(encrypted);
    }

    private SecretKey generateKey(String salt, String passPhrase) {
        try {
            SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
            byte[] saltHex = Hex.decodeHex(salt.toCharArray());
            KeySpec spec = new PBEKeySpec(passPhrase.toCharArray(), saltHex, this.iterationCount, this.keySize);
            return new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
        } catch (InvalidKeySpecException | DecoderException | NoSuchAlgorithmException var6) {
        }
        return null;
    }

    private byte[] doFinal(int encryptMode, SecretKey key, String iv, byte[] bytes) {
        try {
            byte[] ivHex = Hex.decodeHex(iv.toCharArray());
            this.cipher.init(encryptMode, key, new IvParameterSpec(ivHex));
            return this.cipher.doFinal(bytes);
        } catch (InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException | DecoderException | InvalidKeyException var6) {
        }
        return null;
    }

    public static String getReturn(Integer code, String msg, Object data, String type){
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("errcode", code);
        jsonObject.put("msg", msg);
        jsonObject.put("data", data);
        log.info("--返回结果：{}", jsonObject);
        if ("1".equals(type)) {
            return new String(Base64.encodeBase64(jsonObject.toJSONString().getBytes()));
        } else {
            return AESUtils.getInstance().encrypt(jsonObject.toJSONString());
        }
    }

    public static String getParam(String msg, String type){
        if ("1".equals(type)) {
            return new String(Base64.decodeBase64(msg.getBytes()));
        } else {
            return AESUtils.getInstance().decrypt(msg);
        }
    }
}
